#include "ConvexHullSimulationStepper.h"


ConvexHullSimulationStepper::ConvexHullSimulationStepper(const std::vector<clan::Vec2d>& points)
	: m_points(points),
	m_workUnits(),
	m_pointsOfInterest(),
	m_convexHullLines()
{
	m_workUnits.push( new ConvexHullWorkUnit(m_points, 0, m_points.size() - 1));//push whole tree as initial work unit
}

ConvexHullSimulationStepper::~ConvexHullSimulationStepper( void )
{
}

void ConvexHullSimulationStepper::Step( void )
{
	if(!m_workUnits.empty())
	{
		ConvexHullWorkUnit* workUnitActual = m_workUnits.top();
		switch (workUnitActual->GetState())
		{
		case ConvexHullWorkUnitState::Initial:
			if(workUnitActual->HullIsBruteForceComputable())
			{
				workUnitActual->BruteForceComputeHull();
				m_workUnits.pop();//TODO: pop it
				m_RecomputeConvexHullLines();
				m_RecomputePointsOfInterest();
			}
			else
			{
				workUnitActual->GenerateChildren();
				m_workUnits.push(workUnitActual->GetChildRight());
				m_workUnits.push(workUnitActual->GetChildLeft());
				m_RecomputePointsOfInterest();
			}
			break;
		case ConvexHullWorkUnitState::WaitingForChildren:
			workUnitActual->StartChildrenMerger();
			m_RecomputeConvexHullLines();
			break;
		case ConvexHullWorkUnitState::Merging:
			workUnitActual->StepMerge();
			m_RecomputeConvexHullLines();
			if(workUnitActual->GetState() == ConvexHullWorkUnitState::Finished)
			{
				m_workUnits.pop();
				if(m_workUnits.empty())
				{
					m_pointsOfInterest.clear();
				}
				else
				{
					m_RecomputePointsOfInterest(); 
				}
			}
			break;
		default:
			break;
		}
	}
}

void ConvexHullSimulationStepper::Run( void )
{
	CalcRecursive();	
}

void ConvexHullSimulationStepper::CalcRecursive( void )
{
	if (!m_workUnits.empty())
	{
		ConvexHullWorkUnit* workUnitActual = m_workUnits.top();

		if (workUnitActual->HullIsBruteForceComputable())
		{
			// Terminate Recursion here
			workUnitActual->BruteForceComputeHull();
			m_workUnits.pop();//TODO: pop it
			m_RecomputeConvexHullLines();
			m_RecomputePointsOfInterest();
		}
		else
		{
			// Do recursion here
			workUnitActual->GenerateChildren();
			m_workUnits.push(workUnitActual->GetChildRight());
			m_RecomputePointsOfInterest();
			CalcRecursive();									// Recursion for right branch
			m_workUnits.push(workUnitActual->GetChildLeft());
			m_RecomputePointsOfInterest();
			CalcRecursive();									// Recursion for left branch
			
			// After at least two leafs from the same branch are complete, merge here
			workUnitActual = m_workUnits.top();					// Set pointer to current leaf

			workUnitActual->StartChildrenMerger();

			while(workUnitActual->GetState() != ConvexHullWorkUnitState::Finished) {
				workUnitActual->StepMerge();
			}

			m_RecomputeConvexHullLines();
			m_workUnits.pop();
			if(m_workUnits.empty())
			{
				m_pointsOfInterest.clear();
			}
			else
			{
				m_RecomputePointsOfInterest();
			}
		}
	}
}

void ConvexHullSimulationStepper::m_RecomputePointsOfInterest( void )
{
	m_pointsOfInterest = m_workUnits.top()->GetPointsOfInterest();
}


std::vector<clan::Vec2d> ConvexHullSimulationStepper::GetPointsOfInterest( void ) const
{
	return m_pointsOfInterest;
}

std::vector<clan::Line2d> ConvexHullSimulationStepper::GetConvexHullLines( void ) const
{
	return m_convexHullLines;
}

void ConvexHullSimulationStepper::m_RecomputeConvexHullLines( void )
{
	m_convexHullLines.clear();
	std::stack<ConvexHullWorkUnit*> workUnitsPolled;
	while(! m_workUnits.empty())
	{
		ConvexHullWorkUnit* workUnitActual = m_workUnits.top();
		std::vector<clan::Line2d> convexHullLinesActual = workUnitActual->GetConvexHullLines();
		m_convexHullLines.insert(m_convexHullLines.end(), convexHullLinesActual.begin(), convexHullLinesActual.end());
		workUnitsPolled.push(workUnitActual);
		m_workUnits.pop();
	}
	while(!workUnitsPolled.empty())
	{
		m_workUnits.push(workUnitsPolled.top());
		workUnitsPolled.pop();
	}
}
